﻿using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using IInject.Internal;

namespace IInject.Collections
{
	public class TypedCollection<T> : DisposableContainerMember, IEnumerable<T>
	{
		#region Fields

		private ConcurrentDictionary<Type, T> _collection = new ConcurrentDictionary<Type, T>();

		#endregion

		#region Constructors

		public TypedCollection(IContainer container) : base(container)
		{
		}

		#endregion

		#region Properties

		public int Count
		{
			get
			{
				return _collection.Count;
			}
		}

		public T this[Type key]
		{
			get
			{
				return _collection[key];
			}
		}

		#endregion

		#region Methods

		public T Get(Type type)
		{
			if (_collection.ContainsKey(type))
			{
				return _collection[type];
			}

			return default(T);
		}

		public void Add(Type type, T item)
		{
			DoUntil(() => _collection.TryAdd(type, item));
		}

		public bool Remove(Type type)
		{
			bool result = false;

			if (_collection.ContainsKey(type))
			{
				T value = default(T);

				DoUntil(() =>
				{
					if (_collection.TryRemove(type, out value))
					{
						return (result = true);
					}

					return false;
				});
			}

			return result;
		}

		public void Clear()
		{
			_collection.Clear();
		}

		public bool Contains(Type type)
		{
			return _collection.ContainsKey(type);
		}

		public IEnumerator<T> GetEnumerator()
		{
			return _collection.Values.GetEnumerator();
		}

		IEnumerator IEnumerable.GetEnumerator()
		{
			return GetEnumerator();
		}

		protected void DoUntil(Func<bool> func)
		{
			DoUntil(10, func);
		}

		protected void DoUntil(int count, Func<bool> func)
		{
			var counter = 0;

			while (counter++ < count)
			{
				if (func()) { return; }
			}

			// TODO: Maybe throw exception...couldn't add the item. bummer!
		}

		protected override void Dispose(bool disposing)
		{
			if (disposing)
			{
				foreach (var item in _collection.Values.OfType<IDisposable>())
				{
					if (item == null)
					{
						continue;
					}

					item.Dispose();
				}

				_collection.Clear();
			}
		}

		#endregion
	}
}
